package nmf;

import java.util.ArrayList;
import java.util.HashMap;

import org.ejml.simple.SimpleMatrix;

public abstract class FeatureRelevanceTool extends NMFTool {
   
   public int trainClass[] = null, numberOfCategories;
   public ArrayList<Integer[]> categoriesTopWords;
   public Integer[] topWords;
   public HashMap<Integer, Integer> map;
   
   @Override
   public void initialize(final SimpleMatrix V, final SimpleMatrix W, final SimpleMatrix H, final int[] trainingClass) {
      trainClass = trainingClass;
      numberOfCategories = getNumCategories(trainClass);
      map = new HashMap<Integer, Integer>();
      
      for (final int trainClas : trainClass) {
         if (map.containsKey(trainClas)) {
            map.put(trainClas, map.get(trainClas) + 1);
         }
         else {
            map.put(trainClas, 1);
         }
      }
      
      initializeH(V, H);
      initializeW(V, W, H);
   }
   
   /**
    * To initialize H, this method copies an equal number of rows corresponding to
    * the most relevant words in V into H as features.
    * 
    * @param V
    * @param H
    */
   private void initializeH(final SimpleMatrix V, final SimpleMatrix H) {
      final int numberOfFeatures = H.numRows() / numberOfCategories;
      categoriesTopWords = new ArrayList<Integer[]>();
      for (int currentCategory = 1; currentCategory <= numberOfCategories; currentCategory++) {
         categoriesTopWords.add(getRelevantFeatures(V, currentCategory, numberOfFeatures));
      }
      
      H.set(.01);
      
      for (int relevanceLevel = 0; relevanceLevel < numberOfFeatures; relevanceLevel++) {
         for (int currentCategory = 0; currentCategory < numberOfCategories; currentCategory++) {
            copyRow(H, V, categoriesTopWords.
                  get(currentCategory)[relevanceLevel],
                  numberOfCategories * relevanceLevel + currentCategory);
         }
      }
   }
   
   private void copyRow(final SimpleMatrix H, final SimpleMatrix V, final int rowNumV, final int rowNumH) {
      if (V.numCols() != H.numCols()) {
         System.out.println("Columns of V and H do not match up.\n");
      }
      if (rowNumH > H.numRows()) {
         System.out.println("RowNumH is too large.\n");
      }
      int col;
      try {
         for (col = 0; col < V.numCols(); col++) {
            H.set(rowNumH, col, 100 * V.get(rowNumV, col) + H.get(rowNumH, col));
         }
      }
      catch (final Exception e) {
         System.out.print("Copy Row Error.");
         e.printStackTrace();
      }
   }
   
   public abstract Integer[] getRelevantFeatures(SimpleMatrix V, int category,
         int numberOfFeatures);
   
   protected Double getWordRelevance(final SimpleMatrix V, final int wordsRow, final int category) {
      Double wordCatNorm = 0.0, antiCatNorm = 0.0;
      int numberOfDocsinCat = 0;
      
      for (int currentDoc = 0; currentDoc < trainClass.length; currentDoc++) {
         if (trainClass[currentDoc] == category) {
            wordCatNorm += V.get(wordsRow, currentDoc);
            numberOfDocsinCat++;
         }
         else {
            antiCatNorm += V.get(wordsRow, currentDoc);
         }
      }
      
      wordCatNorm /= numberOfDocsinCat;
      antiCatNorm /= trainClass.length - numberOfDocsinCat;
      
      return wordCatNorm - antiCatNorm;
   }
   
   private void initializeW(final SimpleMatrix V, final SimpleMatrix W, final SimpleMatrix H) {
      W.set(epsilon * 1000);
      
      //for (int colNum = 0; colNum < W.numCols(); colNum++)
      //W.set(colNum, colNum, 1);
      
      /*      for (int i = 0; i < W.numCols(); i++) {
         copyCol(W, V, i, i);
      }*/
      
      //SimpleMatrix forcedSquare = H.mult(H.transpose());
      //if (forcedSquare.determinant() != 0)
      // W.set(V.mult(H.transpose()).mult(forcedSquare.invert()));
      // else {
      //  System.out.println("Initialization for W not possible.");
      //
      //RandomMatrices.setRandom(W.getMatrix(), new Random());
      // }
   }
   
   public int getNumCategories(final int[] trainClass) {
      final HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
      int numberOfCategories = 0;
      for (int i = 0; i < trainClass.length; i++) {
         if (map.get(trainClass[i]) == null) {
            numberOfCategories++;
            map.put(trainClass[i], i);
         }
      }
      
      return numberOfCategories;
   }
   
}
